package com.cnpc.ds.annotation;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.net.NetUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import com.cnpc.ds.entity.CommCrustressLog;
import com.cnpc.ds.service.ICommCrustressLogService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.time.LocalDateTime;

/**
 * 日志记录
 *
 * @author jyq
 * @create 2022-06-15 10:50
 */
@Aspect
@Component
@Slf4j
public class LogAspect {

    @Value("${spring.application.name}")
    private String appCode;

    @Autowired
    ICommCrustressLogService commCrustressLogService;

    @Pointcut("execution(public * com.cnpc.edesign.controller.*.*(..))")
    public void logPointCut() {
    }

    @Before("logPointCut()")
    public void doBefore(JoinPoint joinPoint) {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        request.setAttribute("time-id",System.currentTimeMillis());
    }

    /**
     * 操作成功返回结果记录日志
     *
     * @author xuyuxiang
     * @date 2020/3/20 11:51
     */
    @AfterReturning(pointcut = "logPointCut()", returning = "result")
    public void doAfterReturning(JoinPoint joinPoint, Object result) {
        this.log(joinPoint, null, result);
    }


    @AfterThrowing(pointcut = "logPointCut()", throwing = "exception")
    public void saveExceptionLog(JoinPoint joinPoint, Exception exception) throws Throwable {
        this.log(joinPoint, exception, null);
    }

    private void log(JoinPoint joinPoint, Exception exception, Object result) {
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        HttpServletRequest request = sra.getRequest();
        long time=System.currentTimeMillis()- Convert.toLong(request.getAttribute("time-id"));
        //保存日志
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletResponse response = requestAttributes.getResponse();

        //从切面织入点处通过反射机制获取织入点处的方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();

        //获取切入点所在的方法
        Method method = signature.getMethod();

        CommCrustressLog systemLog = new CommCrustressLog();
        //获取操作
        //获取请求的类名
        String className = joinPoint.getTarget().getClass().getName();
        //获取请求的方法名
        String methodName = method.getName();
        systemLog.setCreateDate(LocalDateTime.now());
        systemLog.setIp(NetUtil.getLocalhostStr());
        systemLog.setMethod(request.getMethod());
        //项目名称
        systemLog.setAppCode(appCode);

        if("GET".equals(request.getMethod().toUpperCase())){
            systemLog.setRequestParam(JSONObject.toJSONString(request.getParameterMap()));
        }else {
            String param = LogAspect.getArgsJsonString(joinPoint);
            systemLog.setRequestParam(param);
        }

        systemLog.setRequestUrl(request.getRequestURL().toString());
        systemLog.setRequestPath(className + "." + methodName);
        systemLog.setHttpStatus(response.getStatus() + "");

        //异常信息
        if (exception != null) {
            systemLog.setResult(exception.getMessage());
            //异常具体错误
            StringBuffer str = new StringBuffer();
            for (StackTraceElement stackTraceElement : exception.getStackTrace()) {
                str.append(stackTraceElement.toString() + "\n");
            }
            systemLog.setMsg(str.length() > 1000 ? str.substring(0, 1000) : str.toString());
            systemLog.setIsOk(0);
        }
        //正常日志
        else if (result != null) {
            String s = JSONObject.toJSONString(result);
            systemLog.setResult("");
            systemLog.setMsg(s.length() > 1000 ? s.substring(0, 1000) : s);
            systemLog.setIsOk(1);
        }

        //请求信息 过长切割
        if(systemLog.getRequestParam()!=null && systemLog.getRequestParam().length()>2000){
            systemLog.setRequestParam(systemLog.getRequestParam().substring(0, 2000));
        }
        systemLog.setTime(time);
        ThreadUtil.execAsync(() -> {
            try{
                //子线程共享主线程http里面所有参数
                RequestContextHolder.setRequestAttributes(sra, true);
                commCrustressLogService.save(systemLog);
            }catch (Exception e){
                log.error("记录日志错误",e);
            }
        });
    }

    /**
     * 获取切面的参数json
     *
     * @author xuyuxiang
     * @date 2020/3/16 17:57
     */
    public static String getArgsJsonString(JoinPoint joinPoint) {
        StringBuilder argsJson = new StringBuilder();
        Object[] args = joinPoint.getArgs();
        for (Object arg : args) {
            if (!isFilterObject(arg)) {
                if (ObjectUtil.isNotNull(arg)) {
                    String jsonStr = JSONObject.toJSONString(arg);
                    argsJson.append(jsonStr).append(" ");
                }
            }
        }
        return argsJson.toString().trim();
    }

    /**
     * 判断是否需要拼接参数，过滤掉HttpServletRequest,MultipartFile,HttpServletResponse等类型参数
     *
     * @author xuyuxiang
     * @date 2020/3/16 17:59
     */
    private static boolean isFilterObject(Object arg) {
        return arg instanceof MultipartFile || arg instanceof HttpServletRequest || arg instanceof HttpServletResponse;
    }
}